--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 11ccc76d93e04da06ddbefbf3cf9013d75986679
Parents : f2d0ea9
Author : Mark Qvist <mark@unsigned.io>
Date : 2025-12-29T22:21:51+01:00
Added partial-updating links
Changes
4 files changed, 79 insertions(+), 49 deletions(-)
Diff
diff --git a/nomadnet/ui/textui/Browser.py b/nomadnet/ui/textui/Browser.py
index 545a826..b0885b7 100644
--- a/nomadnet/ui/textui/Browser.py
+++ b/nomadnet/ui/textui/Browser.py
@@ -185,6 +185,7 @@ class Browser:
return destination_type
def handle_link(self, link_target, link_data = None):
+ partial_ids = None
request_data = None
if link_data != None:
link_fields = []
@@ -244,6 +245,10 @@ class Browser:
if len(components) == 2:
destination_type = self.expand_shorthands(components[0])
link_target = components[1]
+ elif link_target.startswith("p:"):
+ comps = link_target.split(":")
+ if len(comps) > 1: partial_ids = comps[1:]
+ destination_type = "partial"
else:
destination_type = "nomadnetwork.node"
link_target = components[0]
@@ -264,6 +269,9 @@ class Browser:
RNS.log("Passing LXMF link to handler", RNS.LOG_DEBUG)
self.handle_lxmf_link(link_target)
+ elif destination_type == "partial":
+ if partial_ids != None and len(partial_ids) > 0: self.handle_partial_updates(partial_ids)
+
else:
RNS.log("No known handler for destination type "+str(destination_type), RNS.LOG_DEBUG)
self.browser_footer = urwid.Text("Could not open link: "+"No known handler for destination type "+str(destination_type))
@@ -483,13 +491,13 @@ class Browser:
def detect_partials(self):
for w in self.attr_maps:
o = w._original_widget
- if hasattr(o, "partial_id"):
- RNS.log(f"Found partial: {o.partial_id} / {o.partial_url} / {o.partial_refresh}")
- partial = {"id": o.partial_id, "url": o.partial_url, "fields": o.partial_fields, "refresh": o.partial_refresh,
- "content": None, "updated": None, "update_requested": None, "request_id": None, "destination": None,
- "link": None, "pile": o, "attr_maps": None, "failed": False, "pr_throttle": 0}
+ if hasattr(o, "partial_hash"):
+ RNS.log(f"Found partial: {o.partial_hash} / {o.partial_url} / {o.partial_refresh}")
+ partial = {"hash": o.partial_hash, "id": o.partial_id, "url": o.partial_url, "fields": o.partial_fields,
+ "refresh": o.partial_refresh, "content": None, "updated": None, "update_requested": None, "request_id": None,
+ "destination": None, "link": None, "pile": o, "attr_maps": None, "failed": False, "pr_throttle": 0}
- self.page_partials[o.partial_id] = partial
+ self.page_partials[o.partial_hash] = partial
if len(self.page_partials) > 0: self.start_partial_updater()
@@ -560,7 +568,7 @@ class Browser:
partial["link"] = existing_link
break
- if not partial["link"]:
+ if not partial["link"] or partial["link"].status == RNS.Link.CLOSED:
RNS.log(f"Establishing link for partial: {partial_destination_hash} / {path}", RNS.LOG_EXTREME)
identity = RNS.Identity.recall(partial_destination_hash)
destination = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, self.app_name, self.aspects)
@@ -644,6 +652,19 @@ class Browser:
def start_partial_updater(self):
if not self.updater_running: self.update_partials()
+ def handle_partial_updates(self, partial_ids):
+ RNS.log(f"Update partials: {partial_ids}")
+ def job():
+ for pid in self.page_partials:
+ try:
+ partial = self.page_partials[pid]
+ if partial["id"] in partial_ids:
+ partial["update_requested"] = time.time()
+ self.__load_partial(partial)
+ except Exception as e: RNS.log(f"Error updating page partial: {e}", RNS.LOG_ERROR)
+
+ threading.Thread(target=job, daemon=True).start()
+
def update_partials(self, loop=None, user_data=None):
with self.partial_updater_lock:
def job():
@@ -702,35 +723,25 @@ class Browser:
components = url.split(":")
if len(components) == 1:
if len(components[0]) == (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2:
- try:
- destination_hash = bytes.fromhex(components[0])
- except Exception as e:
- raise ValueError("Malformed URL")
+ try: destination_hash = bytes.fromhex(components[0])
+ except Exception as e: raise ValueError("Malformed URL")
path = Browser.DEFAULT_PATH
- else:
- raise ValueError("Malformed URL")
+ else: raise ValueError("Malformed URL")
elif len(components) == 2:
if len(components[0]) == (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2:
- try:
- destination_hash = bytes.fromhex(components[0])
- except Exception as e:
- raise ValueError("Malformed URL")
+ try: destination_hash = bytes.fromhex(components[0])
+ except Exception as e: raise ValueError("Malformed URL")
path = components[1]
- if len(path) == 0:
- path = Browser.DEFAULT_PATH
+ if len(path) == 0: path = Browser.DEFAULT_PATH
else:
if len(components[0]) == 0:
if self.destination_hash != None:
destination_hash = self.destination_hash
path = components[1]
- if len(path) == 0:
- path = Browser.DEFAULT_PATH
- else:
- raise ValueError("Malformed URL")
- else:
- raise ValueError("Malformed URL")
- else:
- raise ValueError("Malformed URL")
+ if len(path) == 0: path = Browser.DEFAULT_PATH
+ else: raise ValueError("Malformed URL")
+ else: raise ValueError("Malformed URL")
+ else: raise ValueError("Malformed URL")
if destination_hash != None and path != None:
if path.startswith("/file/"):
diff --git a/nomadnet/ui/textui/Guide.py b/nomadnet/ui/textui/Guide.py
index e245fb3..ddb2cb3 100644
--- a/nomadnet/ui/textui/Guide.py
+++ b/nomadnet/ui/textui/Guide.py
@@ -1302,24 +1302,6 @@ Here is `F00f`_`[a more visible link`72914442a3689add83a09a767963f57c:/page/inde
When links like these are displayed in the built-in browser, clicking on them or activating them using the keyboard will cause the browser to load the specified URL.
->Partials
-
-You can include partials in pages, which will load asynchronously once the page itself has loaded.
-
-`Faaa
-`=
-`{f64a846313b874ee4a357040807f8c77:/page/partial_1.mu}
-`=
-``
-
-It's also possible to set an auto-refresh interval for partials. The following partial will update every 10 seconds.
-
-`Faaa
-`=
-`{f64a846313b874ee4a357040807f8c77:/page/refreshing_partial.mu`10}
-`=
-``
-
>Fields & Requests
Nomad Network let's you use simple input fields for submitting data to node-side applications. Submitted data, along with other session variables will be available to the node-side script / program as environment variables.
@@ -1473,6 +1455,36 @@ This line will
``
+>Partials
+
+You can include partials in pages, which will load asynchronously once the page itself has loaded.
+
+`Faaa
+`=
+`{f64a846313b874ee4a357040807f8c77:/page/partial_1.mu}
+`=
+``
+
+It's also possible to set an auto-refresh interval for partials. Omit or set to 0 to disable. The following partial will update every 10 seconds.
+
+`Faaa
+`=
+`{f64a846313b874ee4a357040807f8c77:/page/refreshing_partial.mu`10}
+`=
+``
+
+You can include field values and variables in partial updates, and by setting the `!pid`! variable, you can create links that update one or more specific partials.
+
+`Faaa
+`=
+Name: `B444`<user_name`>`b
+
+`F38a`[Say hello`p:32]`f
+
+`{f64a846313b874e84a357039807f8c77:/page/hello_partial.mu`0`pid=32|user_name}
+`=
+``
+
>Literals
To display literal content, for example source-code, or blocks of text that should not be interpreted by micron, you can use literal blocks, specified by the \\`= tag. Below is the source code of this entire document, presented as a literal block.
diff --git a/nomadnet/ui/textui/MicronParser.py b/nomadnet/ui/textui/MicronParser.py
index 7ca715e..0599216 100644
--- a/nomadnet/ui/textui/MicronParser.py
+++ b/nomadnet/ui/textui/MicronParser.py
@@ -93,6 +93,7 @@ def parse_partial(line):
else:
partial_data = line[0:endpos]
+ partial_id = None
partial_components = partial_data.split("`")
if len(partial_components) == 1:
partial_url = partial_components[0]
@@ -111,15 +112,21 @@ def parse_partial(line):
partial_fields = ""
partial_refresh = None
- if partial_refresh and partial_refresh < 1: partial_refresh = None
+ if partial_refresh != None and partial_refresh < 1: partial_refresh = None
pf = partial_fields.split("|")
- if len(pf) > 0: partial_fields = pf
+ if len(pf) > 0:
+ partial_fields = pf
+ for f in pf:
+ if f.startswith("pid="):
+ pcs = f.split("=")
+ partial_id = pcs[1]
if len(partial_url):
pile = urwid.Pile([urwid.Text(f"⧖")])
partial_descriptor = "|".join(partial_components)
- pile.partial_id = RNS.hexrep(RNS.Identity.full_hash(partial_descriptor.encode("utf-8")), delimit=False)
+ pile.partial_id = partial_id
+ pile.partial_hash = RNS.hexrep(RNS.Identity.full_hash(partial_descriptor.encode("utf-8")), delimit=False)
pile.partial_url = partial_url
pile.partial_fields = partial_fields
pile.partial_refresh = partial_refresh
diff --git a/nomadnet/util.py b/nomadnet/util.py
index 1a31304..0d8c79f 100644
--- a/nomadnet/util.py
+++ b/nomadnet/util.py
@@ -1,7 +1,7 @@
import re
import unicodedata
-invalid_rendering = ["🕵️"]
+invalid_rendering = ["🕵️", "☝"]
def strip_modifiers(text):
def process_characters(text):
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────